package com.example.demo.aop;

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

/**
 *功能描述: aop基本实现 前置后置环绕
 * @author yuwc
 *备注
 */
@Aspect
@Component
public class HttpRequestAspect {
    /**
     * 注意logger引入类型 引入slf4j包
     */
   private static final Logger log = LoggerFactory.getLogger(HttpRequestAspect.class);
   /**
    *功能描述:记录访问借口时间
    *备注：ThreadLocal 线程独有  线程本地变量
    */
    ThreadLocal<AopRecord> local = new ThreadLocal<>();

    /**
     * PointCut注解表示表示横切点，哪些方法需要被横切
     * 切点表达式 切点签名
     *
     * @Pointcut("execution(public * com.example.demo.controller.UserController.find*(..))")
     */
    @Pointcut("execution(public * com.example.demo.controller.UserController.*(..))")
    public void webLog() {

    }


    /**
     *功能描述:前置通知
     * @author yuwc
     * @param  * @param joinPoint 
     * @return 
     *备注       
     */
    @Before("webLog()")
    public void before(JoinPoint joinPoint) throws InterruptedException {
        log.info("前置切面before……");
        //获取目标方法的参数信息
        Object[] obj = joinPoint.getArgs();
        //AOP代理类的信息
        joinPoint.getThis();
        //代理的目标对象
        joinPoint.getTarget();
        //用的最多 通知的签名
        Signature signature = joinPoint.getSignature();
        //代理的是哪一个方法
        log.info("代理的是哪一个方法"+signature.getName());
        //AOP代理类的名字
        log.info("AOP代理类的名字"+signature.getDeclaringTypeName());
        //AOP代理类的类（class）信息
        signature.getDeclaringType();
        //获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        //如果要获取Session信息的话，可以这样写：
        HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION);
        //获取请求参数
        Enumeration<String> enumeration = request.getParameterNames();
        Map<String,String> parameterMap = new HashMap<>(40);
        while (enumeration.hasMoreElements()){
            String parameter = enumeration.nextElement();
            parameterMap.put(parameter,request.getParameter(parameter));
        }
        String str = JSON.toJSONString(parameterMap);
        if(obj.length > 0) {
            log.info("请求的参数信息为："+str);
        }
        //获取记录对象
        AopRecord arb=local.get();
        if(arb == null){
            arb=new AopRecord();
            local.set(arb);
        }
        arb.setStartTime(System.currentTimeMillis());
        String requestURI = request.getRequestURI();
        String remoteAddr = request.getRemoteAddr();
        String requestMethod = request.getMethod();
        String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        log.info("请求url=" + requestURI + ",客户端ip=" + remoteAddr + ",请求方式=" + requestMethod + ",请求的类名=" + declaringTypeName + ",方法名=" + methodName + ",入参=" + args);
    }

    /**
     *功能描述:后置通知
     * @author yuwc
     * @param  * @param
     * @return
     *备注@After注解表示在方法执行之后执行
     */
    @After("webLog()")
    public void after() {
        AopRecord arb=local.get();
        if(arb == null){
            arb=new AopRecord();
            local.set(arb);
        }
        arb.setEndTime(System.currentTimeMillis());
        log.info("后置切面after……");
    }

    /**
     *功能描述: 获取方法的返回值
     * @author yuwc
     * @param  * @param object
     * @return
     *备注 @AfterReturning注解用于获取方法的返回值
     */
    @AfterReturning(pointcut = "webLog()", returning = "object")
    public void getAfterReturn(Object object) {
        if (object != null) {
            AopRecord arb=local.get();
            if(arb == null){
                arb=new AopRecord();
                local.set(arb);
            }
            log.info("本次接口耗时="+arb.getUseTime()+"ms");
            log.info("afterReturning="+object.toString());
            local.remove();
        }
    }

    /**
     *功能描述: 环绕通知
     * @author yuwc
     * @param  * @param proceedingJoinPoint
     * @return
     *备注    环绕通知非常强大，可以决定目标方法是否执行，什么时候执行，执行时是否需要替换方法参数，执行完毕是否需要替换返回值。
     *  环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
     *  环绕的是方法 后置在方法后执行 先环绕之后后置
     *
     *  和ControllerAdvice 共存 这里try异常了 ， 加强控制器 无法获取到异常信息  GlobleExceptionHandler
     */
    @Around("webLog()")
    public Object doAroundService(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("开始：环绕通知的目标方法名为 ： "+proceedingJoinPoint.getSignature().getName());
        Object object=proceedingJoinPoint.proceed();
        System.out.println("结束：环绕通知的目标方法名为 ： "+proceedingJoinPoint.getSignature().getName());
        return object;
//        try {
//
//        }
//        catch (Throwable throwable) {
//            throwable.printStackTrace();
//        }
//        System.out.println("结束：环绕通知的目标方法名为 ： "+proceedingJoinPoint.getSignature().getName());
//        return  null;
    }

}
